#include <stdlib.h>
#include "origin.h"

Origin::Origin(int width, int height) :
	pmodel(NULL), w_width(width), w_height(height) {
	spin.x = 0;
	spin.y = 0;

	anchor = spin;
	lastspin = spin;
}

Origin::~Origin() {
}

/*
 * compute the bary centric cooridinates
 * */
bool Origin::baryCoord(float m, float n, float* x, float* z, float* b)
{
	float a;
	a=x[0]*z[1]+x[1]*z[2]+x[2]*z[0]-x[0]*z[2]-x[1]*z[0]-x[2]*z[1];

	b[0]=(m*z[1]+x[1]*z[2]+x[2]*n-m*z[2]-x[1]*n-x[2]*z[1])/a;
	b[1]=(x[0]*n+m*z[2]+x[2]*z[0]-x[0]*z[2]-m*z[0]-x[2]*n)/a;
	b[2]=(x[0]*z[1]+x[1]*n+m*z[0]-x[0]*n-x[1]*z[0]-m*z[1])/a;
	if(b[0]<0||b[1]<0||b[2]<0)
		return false;
	return true;
}
floatbuffer*
Origin::getdepthbuffer() {
	floatbuffer *zbuf = new floatbuffer();

	zbuf->create(w_width, w_height);
	//map (-1,-1,-1) and (1,1,1) bounding box to (0, -1, width) (0,1,height);
	//update the buffer for each triangle in the model.

	GLMgroup* group;
	GLMtriangle* triangle;
	group = pmodel->groups;
	float x[3], y[3], z[3], b[3];//x, y ,z and barcentric cooridnates

	for (int i = 0; i < pmodel->numtriangles; i++) {
		triangle = &pmodel->triangles[i];
		for (int v = 0; v < 3; v++) {
			x[v] = pmodel->vertices[triangle->vindices[v] * 3];
			y[v] = pmodel->vertices[triangle->vindices[v] * 3 + 1];

			z[v] = pmodel->vertices[triangle->vindices[v] * 3 + 2];
			x[v] = (x[v] + 1) / 2 * w_width;
			z[v] = (z[v] + 1) / 2 * w_height;
		}

		int minx, maxx, minz, maxz;
		minx= min3(x)+0.5;
		minz= min3(z)+0.5;
		maxx= max3(x)+0.5;
		maxz= max3(z)+0.5; //+0.5 to round to nearest integer
		for(int m=minx;m<=maxx;m++)
			for(int n=minz; n<=maxz;n++)
			{
				if(baryCoord(m,n,x,z,b))
				{
					float ym=y[0]*b[0]+y[1]*b[1]+y[2]*b[2];
					if(ym>zbuf->getBuf(m,n))
					zbuf->setBuf(m,n,ym);
				}
			}

	}

	//std::cerr << "at getdepthbuffer" << endl;
	return zbuf;
}


void Origin::drawmodel(void) {
	if (!pmodel) {
		char filename[1000];
		sprintf(filename, "%s%s", rootdir, "data/al.obj");
		pmodel = glmReadOBJ(filename);
		if (!pmodel)
			exit(0);
		glmUnitize(pmodel);
		glmFacetNormals(pmodel);
		glmVertexNormals(pmodel, 90.0);
	}

	glmDraw(pmodel, GLM_SMOOTH | GLM_MATERIAL);
}

void Origin::display(void) {
	glClearColor(0.5f, 0.5f, 1.0f, 1.0f);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glRotatef(spin.y, 1.0, 0.0, 0.0);
	glRotatef(spin.x, 0.0, 1.0, 0.0);
	drawmodel();
	glPopMatrix();

	glutSwapBuffers();
	//   glutPostRedisplay();
}

void Origin::keyboard(unsigned char key, int x, int y) {

}

void Origin::mouse(int button, int state, int x, int y) {
	switch (state) {
	case GLUT_DOWN:
		anchor.x = x;
		anchor.y = y;
		break;
	case GLUT_UP:
		lastspin = spin;
		break;
	}
	glutPostRedisplay();
}

void Origin::motion(int x, int y) {
	spin.x = lastspin.x + x - anchor.x;
	spin.y = lastspin.y + y - anchor.y;

	glutPostRedisplay();
}

void Origin::reshape(int width, int height) {
	w_width = width;
	w_height = height;

	int tx, ty, tw, th;

//	glViewport(0, 0, width, height);
	GLUI_Master.auto_set_viewport();
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(60.0, (GLfloat) height / (GLfloat) width, 1.0, 128.0);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glTranslatef(0.0, 0.0, -3.0);
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
}

void Origin::loadmodel(char* name) {
	pmodel = glmReadOBJ(name);
	if (!pmodel)
		exit(0);
	glmUnitize(pmodel);
	glmFacetNormals(pmodel);
	glmVertexNormals(pmodel, 90.0);
}
